﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace JOATServer.SocietyFramework.Core.XML
{
    public abstract partial class BaseXMLAutomaton : BaseAutomaton
    {
        /* ******************** FUNCION-LIKE INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Abstract class which must be extended in order to create a function-like instruction.
        /// It contains an array of parameters which is used when calling the instruction.
        /// </summary>
        protected abstract class FunctionInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction[] parameters;

            public FunctionInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public virtual object Code()
            {
                return null;
            }
        }

        /// <summary>
        /// Class which represents the getCurrentMessageFrom() instruction. It returns currentMessage.from value.
        /// </summary>
        protected class GetCurrentMessageFromInstruction : FunctionInstruction
        {
            public GetCurrentMessageFromInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return Owner.currentMessage.From;
            }
        }

        /// <summary>
        /// Class which represents the getCurrentMessageData() instruction. It returns currentMessage.data value.
        /// </summary>
        protected class GetCurrentMessageDataInstruction : FunctionInstruction
        {
            public GetCurrentMessageDataInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return Owner.currentMessage.Data;
            }
        }

        /// <summary>
        /// Class which represents the isCurrentMessageNull() instruction. It returns a Boolean that is true if the current
        /// message is null and false otherwise.
        /// </summary>
        protected class IsCurrentMessageNullInstruction : FunctionInstruction
        {
            public IsCurrentMessageNullInstruction(BaseXMLAutomaton owner) : base(owner) { }

            public override object Code()
            {
                return Owner.currentMessage == null;
            }
        }

        /// <summary>
        /// Class which represents the executeTransition() instruction. It returns the transition result.<br/>
        /// Parameters:<br/>
        /// 0 -> instruction returning the name of the transition to be executed
        /// </summary>
        protected class ExecuteTransitionInstruction : FunctionInstruction
        {
            public TransitionGroup owner;

            public ExecuteTransitionInstruction(BaseXMLAutomaton ow) : base(ow) { }

            public override object Code()
            {
                return owner.transitions[parameters[0].Code().ToString()].TransitionCode();
            }
        }

        /// <summary>
        /// Class which represents the size() instruction. It returns the number of elements in a Map as an Integer.<br/>
        /// Parameters:<br/>
        /// 0 -> the Map object
        /// </summary>
        protected class SizeInstruction : FunctionInstruction
        {
            public SizeInstruction(BaseXMLAutomaton ow) : base(ow) { }

            public override object Code()
            {
                VariableInstruction variable = (VariableInstruction) parameters[0];
			    Dictionary<object, object> map = (Dictionary<object, object>) Owner.variables[variable.name];
			    if(variable is VariableWithIndexInstruction){
				    foreach(Instruction index in ((VariableWithIndexInstruction)variable).indexValues){
					    map = (Dictionary<object, object>) map[index.Code()];
				    }
			    }
			    return GetElementsNumber(map);
            }

            public Int32 GetElementsNumber(Dictionary<object, object> map){
			    Int32 number = 0;
                foreach (KeyValuePair<object, object> entry in (Dictionary<object, object>)map)
			    {
				    number++;
				    if(entry.Value is Dictionary<object, object>){
                        number += GetElementsNumber((Dictionary<object, object>)entry.Value);
				    }
			    }
			    return number;
		    }
        }

        /// <summary>
        /// Class which represents the split() instruction. It returns a Map containing the tokens after the split.<br/>
        /// Parameters:<br/>
        /// 0 -> the String to be split
        /// 1 -> the String to match for splitting
        /// </summary>
        protected class SplitInstruction : FunctionInstruction
        {
            public SplitInstruction(BaseXMLAutomaton ow) : base(ow) { }

            public override object Code()
            {
                String strSplit = (String)parameters[0].Code();
                String strMatch = (String)parameters[1].Code();

                String[] splitting = Regex.Split(strSplit, strMatch);
                Dictionary<object, object> map = new Dictionary<object, object>();
                for (Int32 i = 0; i < splitting.Length; i++)
                {
                    map.Add(i, splitting[i]);
                }
                return map;
            }
        }

        /// <summary>
        /// Class which represents the parseInteger() instruction. It returns an Integer if parsing was successful or .F. otherwise.<br/>
        /// Parameters:<br/>
        /// 0 -> the Integer to be parsed
        /// </summary>
        protected class ParseIntegerInstruction : FunctionInstruction
        {
            public ParseIntegerInstruction(BaseXMLAutomaton ow) : base(ow) { }

            public override object Code()
            {
                string str = (string)parameters[0].Code();
                try
                {
                    Int32 parsedInteger = Int32.Parse(str);
                    return parsedInteger;
                }
                catch (Exception e)
                {
                    return false;
                }
            }
        }


        /* ******************** SPECIAL INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Class which represents an expression and doesn't return. It is used as a convenient class to manage
        /// situations when some expression must not return a value. The code() function executes the expression
        /// like a normal expression, then returns null.
        /// </summary>
        protected class ExpressionContainerInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction expression;

            public object Code()
            {
                expression.Code();
                return null;
            }

        }

        /// <summary>
        /// Class which represents the get_next_message instruction. It removes a message from the message queue and assigns it
        /// to the currentMessage variable. The currentMessage variable can be accessed in the XML via getCurrentMessageFrom()
        /// and getCurrentMessageData() instructions. The timer represents an instruction which returns the amount of milliseconds
        /// to wait until the function returns. If no message is available after the timer expires then the currentMessage will 
        /// be null. Use isCurrentMessageNull() instruction to test if the last message was received.
        /// </summary>
        protected class GetNextMessageInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction timer;

            public GetNextMessageInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                if (timer == null)
                {
                    Owner.currentMessage = Owner.messageList.Remove();
                }
                else
                {
                    Owner.currentMessage = Owner.messageList.Remove((Int32)timer.Code());
                }
                return null;
            }
        }

        /// <summary>
        /// Class which represents the send_message instruction. It sends a message containing the data expression
        /// to the automaton identified with the to expression.
        /// </summary>
        protected class SendMessageInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction to, data;

            public SendMessageInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                MessageRouter.Instance.Send(to.Code().ToString(),
                        new Message(Owner.Name, data.Code()));
                return null;
            }

        }
    }
}
